// $URL: http://mingw-lib.googlecode.com/svn/trunk/working/avcodec_to_widget_8/t_box_input_youtube.h $
// $Rev: 295 $
// $Author: akio.miyoshi $
// $Date:: 2010-07-28 09:25:49 +0000#$

#ifndef T_BOX_INPUT_YOUTUBE_H
#define T_BOX_INPUT_YOUTUBE_H

#include "t_box_input.h"
#include "nbr.h"

class T_Box_Input_Youtube_Reply : public QObject
{
    Q_OBJECT
public:
    explicit T_Box_Input_Youtube_Reply(const QNetworkRequest &a_request, qint64 a_begin, qint64 a_length = -1)
        : m_request(a_request), m_begin(a_begin), m_length(a_length)
        , m_nam(0), m_reply(0), m_waiter(0)
    {
        Q_ASSERT(m_begin>=0);
        Q_ASSERT(m_length!=0);
    }
    ~T_Box_Input_Youtube_Reply()
    {
        delete m_waiter;
        delete m_reply;
        delete m_nam;
    }
    bool isActive()
    {
        return (m_waiter != NULL);
    }
    void wake()
    {
        if(this->isActive())
        {
            return;
        }
        //[Range header]
        QString v_range = QString("bytes=%1-").arg(m_begin);
        if(m_length > 0)
        {
            v_range += QString::number(m_begin+m_length-1);
        }
        qDebug() << v_range;
        m_request.setRawHeader("Range", v_range.toLatin1());
        //
        m_nam = new QNetworkAccessManager();
        redo:
        m_reply = m_nam->get(m_request);
        m_waiter = new NetworkBuffer(m_reply);
        m_waiter->setName(QString("waiter@%1").arg(m_begin));
        connect(m_reply, SIGNAL(finished()), SIGNAL(finished()));
        qDebug() << "before m_waiter->waitBytesAvailable(0)";
        m_waiter->wait_bytesAvailable(0);
        qDebug() << "after m_waiter->waitBytesAvailable(0)";
        Q_ASSERT(m_reply);
        qDebug() << "HTTP Status" << m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        QVariant v_loc = m_reply->header(QNetworkRequest::LocationHeader);
        if(v_loc.isValid())
        {
            qDebug() << "[v_loc]" << v_loc;
            delete m_waiter;
            m_waiter = NULL;
            delete m_reply;
            m_reply = NULL;
            m_request.setUrl(v_loc.toUrl());
            goto redo;
        }
        QVariant v_cont_len = m_reply->header(QNetworkRequest::ContentLengthHeader);
        qDebug()<< "[T_Box_Input_Youtube_Reply::wake()]" << m_waiter->name()
                << "[v_cont_len]" << v_cont_len
                ;
        if(m_length > 0)
        {
            Q_ASSERT(v_cont_len.isValid());
            Q_ASSERT(m_length==v_cont_len.toLongLong());
        }
    }
    QByteArray wait_readAll(qint64 a_break_size)
    {
        this->wake();
        return m_waiter->wait_readAll(a_break_size);
    }
protected:
    QNetworkRequest m_request;
    qint64 m_begin;
    qint64 m_length;
    QNetworkAccessManager *m_nam;
    QNetworkReply *m_reply;
    NetworkBuffer *m_waiter;
signals:
    void finished();
};

class T_Box_Input_Youtube : public T_Box_Input
{
    Q_OBJECT
public:
    static const int c_buffer_size = 4096;
    quint8 *m_buffer;
    ////QMutex m_mutex;
    T_RecursiveMutex m_mutex;
    bool m_is_valid;
    QQueue<T_Box_Input_Youtube_Reply *> m_reply_queue;
    T_Box_Input_Youtube(const QString &a_video_id, int a_fmt);
    ~T_Box_Input_Youtube();
    virtual bool isValid();
    virtual void close();
    virtual quint8* getIOBuffer();
    virtual int getIOBufferSize();
protected:
    virtual int read_bytes(uint8_t *buf, int buf_size);
    virtual int64_t seek_getpos();
    virtual int64_t seek_setpos(int64_t offset);
    virtual int64_t seek_length();

    //static const int v_thread_count = 4;
    //static const qint64 v_target = 500000; //500K

    static const int v_thread_count = 8;
    static const qint64 v_target = 500000; //500K

    //static const int v_thread_count = 8;
    //static const qint64 v_target = 200000; //200K

    //static const int v_thread_count = 16;
    //static const qint64 v_target = 200000; //200K

    //static const qint64 v_target = 1000000;
    //static const qint64 v_target = 100000;
    //static const qint64 v_target = 10000;
    void wakeReplyQueue()
    {
        int v_size = qMin(v_thread_count, m_reply_queue.size());
        for(int i=0; i<v_size; i++)
        {
            m_reply_queue.value(i)->wake();
        }
    }
    QByteArray readAll(qint64 a_break_size)
    {
        Q_ASSERT(a_break_size>=0);
        QByteArray v_bytes;
        while(v_bytes.size()<a_break_size)
        {
            if(m_reply_queue.size()==0)
            {
                return v_bytes;
            }
            T_Box_Input_Youtube_Reply *v_head = m_reply_queue.head();
            Q_ASSERT(v_bytes.size()<a_break_size);
            qint64 v_wait = a_break_size-v_bytes.size();
            Q_ASSERT(v_wait>0);
            v_bytes.append(v_head->wait_readAll(v_wait));
            if(v_bytes.size()<a_break_size)
            {
                m_reply_queue.dequeue();
                //v_head->deleteLater();
                //delete v_head;
                wakeReplyQueue();
            }
        }
        return v_bytes;
    }
    class T_Box_Input_YoutubePrivate *d_ptr;
    Q_DECLARE_PRIVATE(T_Box_Input_Youtube)
    friend class T_Box_Input_Youtube_Thread;
};

#endif // T_BOX_INPUT_YOUTUBE_H
